home *** CD-ROM | disk | FTP | other *** search
/ GFX Sensations 1 / Graphic Sensations - Volume 1.iso / tools / amiga / 3d_tools / irit40s.lha / Irit / irit / objects.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-30  |  32.6 KB  |  1,131 lines

  1. /*****************************************************************************
  2. *   "Irit" - the 3d (not only polygonal) solid modeller.             *
  3. *                                         *
  4. * Written by:  Gershon Elber                Ver 0.2, Mar. 1990   *
  5. ******************************************************************************
  6. *   Module to handle the objects list - fetch, insert, delete etc...         *
  7. *****************************************************************************/
  8.  
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <math.h>
  12. #include "program.h"
  13. #include "allocate.h"
  14. #include "attribut.h"
  15. #include "geomat3d.h"
  16. #include "objects.h"
  17. #include "windows.h"
  18.  
  19. #define IS_FLOAT_PRINTF_CMD(c)    ((c) == 'e' || (c) == 'f' || (c) == 'g' || \
  20.                  (c) == 'E' || (c) == 'F')
  21. #define IS_PRINTF_CMD(c)  (IS_FLOAT_PRINTF_CMD(c) || \
  22.                (c) == 'd' || (c) == 'i' || (c) == 'u' || \
  23.                (c) == 'o' || (c) == 'x' || (c) == 'X' || \
  24.                (c) == 's' || (c) == 'p' || (c) == 'v' || \
  25.                (c) == 'P' || (c) == 'D')
  26.  
  27. static int GetDumpLevel(void);
  28. static IPPolygonStruct *GenAxesObjectPolylines(void);
  29. static void PrintOneLine(char *Line);
  30. static void UpdateLoadedHierarchy(IPObjectStruct *PObj, IPObjectStruct *Root);
  31. static int MatGenMatGeneric(IPObjectStruct *LstObjList, MatrixType Mat);
  32.  
  33. /*****************************************************************************
  34. *   Routine to set up all the predefined objects - objects that the system   *
  35. * must have all the time, like global transformation matrix.             *
  36. *****************************************************************************/
  37. void SetUpPredefObjects(void)
  38. {
  39.     RealType R;
  40.     MatrixType Mat1, Mat2;
  41.     IPObjectStruct *PObj;
  42.  
  43.     /* 90 - 35.2644 = 54.7356 */
  44.     MatGenMatRotX1(DEG2RAD(-54.7356), Mat1); /* Generate default view trans. */
  45.     MatGenMatRotZ1(M_PI+M_PI/4, Mat2);         /* which is isometric view. */
  46.     MatMultTwo4by4(Mat2, Mat2, Mat1);
  47.     PObj = GenMatObject("VIEW_MAT", Mat2, NULL);
  48.     InsertObject(PObj);
  49.  
  50.     MatGenUnitMat(Mat1);          /* Generate default perspective trans. */
  51.     Mat1[2][2] = 0.1;
  52.     Mat1[2][3] = -0.35;
  53.     Mat1[3][2] = 0.35;
  54.     PObj = GenMatObject("PRSP_MAT", Mat1, NULL);
  55.     InsertObject(PObj);
  56.  
  57.     R = DEFAULT_RESOLUTION;
  58.     PObj = GenNumObject("RESOLUTION", &R, NULL);
  59.     InsertObject(PObj);
  60.  
  61.     R = DEFAULT_DRAW_CTLPT;
  62.     PObj = GenNumObject("DRAWCTLPT", &R, NULL);
  63.     InsertObject(PObj);
  64.  
  65.     R = DEFAULT_INTERCRV;
  66.     PObj = GenNumObject("INTERCRV", &R, NULL);
  67.     InsertObject(PObj);
  68.  
  69.     R = DEFAULT_ECHOSRC;
  70.     PObj = GenNumObject("ECHOSRC", &R, NULL);
  71.     InsertObject(PObj);
  72.  
  73.     R = DEFAULT_DUMPLVL;
  74.     PObj = GenNumObject("DUMPLVL", &R, NULL);
  75.     InsertObject(PObj);
  76.  
  77.     R = 0;
  78.     PObj = GenNumObject("FLAT4PLY", &R, NULL);
  79.     InsertObject(PObj);
  80.  
  81.     R = 0;
  82.     PObj = GenNumObject("POLYSORT", &R, NULL);
  83.     InsertObject(PObj);
  84.  
  85.     R = 0;
  86.     PObj = GenNumObject("COPLANAR", &R, NULL);
  87.     InsertObject(PObj);
  88.  
  89.     R = MACHINE_UNIX;
  90. #if defined(__MSDOS__) || defined(DJGCC)
  91.     R = MACHINE_MSDOS;
  92. #endif
  93. #if defined(sgi)
  94.     R = MACHINE_SGI;
  95. #endif
  96. #if defined(hpbsd) || defined(hpux) || defined(__hpux)
  97.     R = MACHINE_HP;
  98. #endif
  99. #if defined(sun)
  100.     R = MACHINE_SUN;
  101. #endif
  102. #if defined(apollo)
  103.     R = MACHINE_APOLLO;
  104. #endif
  105. #if defined(OS2GCC)
  106.     R = MACHINE_IBMOS2;
  107. #endif
  108. #if defined(WIN32NT)
  109.     R = MACHINE_IBMNT;
  110. #endif
  111. #if defined(AMIGA)
  112.     R = MACHINE_AMIGA;
  113. #endif
  114.  
  115.     PObj = GenNumObject("MACHINE", &R, NULL);
  116.     InsertObject(PObj);
  117.  
  118.     PObj = GenPolyObject("AXES", GenAxesObjectPolylines(), NULL);
  119.     IP_SET_POLYLINE_OBJ(PObj);              /* Mark it as polyline object. */
  120.     InsertObject(PObj);
  121. }
  122.  
  123. /*****************************************************************************
  124. *   Routine to set an attribute of an object.                     *
  125. *****************************************************************************/
  126. void SetObjectAttrib(IPObjectStruct *PObj, char *Name, IPObjectStruct *Data)
  127. {
  128.     if (IP_IS_STR_OBJ(Data))
  129.     AttrSetObjectStrAttrib(PObj, Name, Data -> U.Str);
  130.     else if (IP_IS_NUM_OBJ(Data)) {
  131.     RealType
  132.         r = Data -> U.R;
  133.  
  134.     if (r == (int) r)
  135.         AttrSetObjectIntAttrib(PObj, Name, (int) r);
  136.     else
  137.         AttrSetObjectRealAttrib(PObj, Name, r);
  138.     }
  139. }
  140.  
  141. /*****************************************************************************
  142. *   Routine to get the value to EchoSrc variable.                 *
  143. *****************************************************************************/
  144. static int GetDumpLevel(void)
  145. {
  146.     int DumpLvl;
  147.     IPObjectStruct
  148.     *PObj = GetObject("DUMPLVL");
  149.  
  150.     if (PObj == NULL || !IP_IS_NUM_OBJ(PObj)) {
  151.     WndwInputWindowPutStr("No numeric object name DumpLvl is defined");
  152.     DumpLvl = DEFAULT_DUMPLVL;
  153.     }
  154.     else
  155.     DumpLvl = (int) (PObj -> U.R);
  156.  
  157.     return DumpLvl;
  158. }
  159.  
  160. /*****************************************************************************
  161. *   Generate an axes system with length of 1 on each axis.             *
  162. *****************************************************************************/
  163. static IPPolygonStruct *GenAxesObjectPolylines(void)
  164. {
  165.     IPPolygonStruct *Pl, *PlHead;
  166.     IPVertexStruct *V;
  167.  
  168.     /* X axis. */
  169.     Pl = PlHead = IPAllocPolygon(0, 0, NULL, NULL);
  170.     Pl -> PVertex = V = IPAllocVertex(0, 0, NULL, NULL);
  171.     V -> Coord[0] = 0.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.0;
  172.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  173.     V -> Coord[0] = 1.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.0;
  174.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  175.     V -> Coord[0] = 1.0;    V -> Coord[1] = 0.1;   V -> Coord[2] = 0.1;
  176.  
  177.     Pl -> Pnext = IPAllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;
  178.     Pl -> PVertex = V = IPAllocVertex(0, 0, NULL, NULL);
  179.     V -> Coord[0] = 1.0;    V -> Coord[1] = 0.1;   V -> Coord[2] = 0.0;
  180.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  181.     V -> Coord[0] = 1.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.1;
  182.  
  183.     /* Y axis.*/
  184.     Pl -> Pnext = IPAllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;
  185.     Pl -> PVertex = V = IPAllocVertex(0, 0, NULL, NULL);
  186.     V -> Coord[0] = 0.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.0;
  187.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  188.     V -> Coord[0] = 0.0;    V -> Coord[1] = 1.0;   V -> Coord[2] = 0.0;
  189.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  190.     V -> Coord[0] = 0.0;    V -> Coord[1] = 1.0;   V -> Coord[2] = 0.06;
  191.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  192.     V -> Coord[0] = 0.04;   V -> Coord[1] = 1.0;   V -> Coord[2] = 0.1;
  193.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  194.     V -> Coord[0] = 0.0;    V -> Coord[1] = 1.0;   V -> Coord[2] = 0.06;
  195.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  196.     V -> Coord[0] =(-0.04); V -> Coord[1] = 1.0;   V -> Coord[2] = 0.1;
  197.  
  198.     /* Z axis.*/
  199.     Pl -> Pnext = IPAllocPolygon(0, 0, NULL, NULL); Pl = Pl -> Pnext;
  200.     Pl -> PVertex = V = IPAllocVertex(0, 0, NULL, NULL);
  201.     V -> Coord[0] = 0.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 0.0;
  202.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  203.     V -> Coord[0] = 0.0;    V -> Coord[1] = 0.0;   V -> Coord[2] = 1.0;
  204.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  205.     V -> Coord[0] = 0.1;    V -> Coord[1] = 0.0;   V -> Coord[2] = 1.0;
  206.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  207.     V -> Coord[0] = 0.0;    V -> Coord[1] = 0.1;   V -> Coord[2] = 1.0;
  208.     V -> Pnext = IPAllocVertex(0, 0, NULL, NULL); V = V -> Pnext;
  209.     V -> Coord[0] = 0.1;    V -> Coord[1] = 0.1;   V -> Coord[2] = 1.0;
  210.  
  211.     return PlHead;
  212. }
  213.  
  214. /*****************************************************************************
  215. *   Returns the type of an object.                         *
  216. *****************************************************************************/
  217. double ThisObjectIs(IPObjectStruct *PObj)
  218. {
  219.     return (double) (PObj -> ObjType);
  220. }
  221.  
  222. /*****************************************************************************
  223. *   General printf routine, IRIT's style, to stdout.                 *
  224. *****************************************************************************/
  225. void IritObjectPrintfStdout(char *CtlStr, IPObjectStruct *PObjLst)
  226. {
  227.     IritObjectPrintf(stdout, CtlStr, PObjLst);
  228. }
  229.  
  230. /*****************************************************************************
  231. *   General printf routine, IRIT's style.                     *
  232. *****************************************************************************/
  233. void IritObjectPrintf(FILE *File, char *CtlStr, IPObjectStruct *PObjLst)
  234. {
  235.     char Buffer[LINE_LEN], Line[LINE_LEN_LONG];
  236.     int i, j, k,
  237.     CrntItem = 0,
  238.     NumOfItems = ListObjectLength(PObjLst);
  239.     IPObjectStruct *CrntObj;
  240.  
  241.     Line[0] = 0;
  242.  
  243.     for (i = 0; i < strlen(CtlStr); i++) {
  244.     if (CtlStr[i] == '%') {
  245.         for (j = 0; j < 10 && !IS_PRINTF_CMD(CtlStr[i]); j++, i++)
  246.         Buffer[j] = CtlStr[i];
  247.         Buffer[j++] = CtlStr[i];
  248.         Buffer[j--] = 0;
  249.  
  250.         if (CrntItem >= NumOfItems) {
  251.         WndwInputWindowPutStr("PRINTF: Not enough objects for printf.");
  252.         return;
  253.         }
  254.         CrntObj = ListObjectGet(PObjLst, CrntItem++);
  255.  
  256.         switch (Buffer[j]) {
  257.         case 'c':
  258.             if (IP_IS_NUM_OBJ(CrntObj)) {
  259.             sprintf(&Line[strlen(Line)], Buffer,
  260.                 (int) CrntObj -> U.R);
  261.             }
  262.             else {
  263.             WndwInputWindowPutStr("PRINTF: Number expected.");
  264.             return;
  265.             }
  266.             break;
  267.         case 'd':
  268.         case 'i':
  269.         case 'u':
  270.         case 'o':
  271.         case 'x':
  272.         case 'X':
  273.             if (IP_IS_NUM_OBJ(CrntObj)) {
  274.             sprintf(&Line[strlen(Line)], Buffer,
  275.                 (int) CrntObj -> U.R);
  276.             }
  277.             else {
  278.             WndwInputWindowPutStr("PRINTF: Number expected.");
  279.             return;
  280.             }
  281.             break;
  282.         case 'e':
  283.         case 'f':
  284.         case 'g':
  285.         case 'E':
  286.         case 'G':
  287.             if (IP_IS_NUM_OBJ(CrntObj)) {
  288.             sprintf(&Line[strlen(Line)], Buffer,
  289.                 CrntObj -> U.R);
  290.             }
  291.             else {
  292.             WndwInputWindowPutStr("PRINTF: Number expected.");
  293.             return;
  294.             }
  295.             break;
  296.         case 's':
  297.             if (IP_IS_STR_OBJ(CrntObj)) {
  298.             sprintf(&Line[strlen(Line)], Buffer,
  299.                 CrntObj -> U.Str);
  300.             }
  301.             else {
  302.             WndwInputWindowPutStr("PRINTF: String expected.");
  303.             return;
  304.             }
  305.             break;
  306.         case 'p':
  307.         case 'v':
  308.             if (!IP_IS_VEC_OBJ(CrntObj) &&
  309.             !IP_IS_POINT_OBJ(CrntObj)) {
  310.             WndwInputWindowPutStr(
  311.                 "PRINTF: Vector or Point expected.");
  312.             return;
  313.             }
  314.             Buffer[j] = CtlStr[++i];
  315.             if (!IS_FLOAT_PRINTF_CMD(Buffer[j])) {
  316.             WndwInputWindowPutStr(
  317.                 "PRINTF: Floaing point number command expected.");
  318.             return;
  319.             }
  320.             for (k = 0; k < 3; k++) {
  321.             sprintf(&Line[strlen(Line)],
  322.                 Buffer, CrntObj -> U.Vec[k]);
  323.             if (k < 2)
  324.                 strcat(Line, ", ");
  325.             }
  326.             break;
  327.         case 'P':
  328.             if (!IP_IS_PLANE_OBJ(CrntObj)) {
  329.             WndwInputWindowPutStr("PRINTF: Plane expected.");
  330.             return;
  331.             }
  332.             Buffer[j] = CtlStr[++i];
  333.             if (!IS_FLOAT_PRINTF_CMD(Buffer[j])) {
  334.             WndwInputWindowPutStr(
  335.                 "PRINTF: Floaing point number command expected.");
  336.             return;
  337.             }
  338.             for (k = 0; k < 4; k++) {
  339.             sprintf(&Line[strlen(Line)],
  340.                 Buffer, CrntObj -> U.Plane[k]);
  341.             if (k < 3)
  342.                 strcat(Line, ", ");
  343.             }
  344.             break;
  345.         case 'D':
  346.             Buffer[j] = CtlStr[++i];
  347.             CagdSetFloatFormat(Buffer);
  348.             IritPrsrSetFloatFormat(Buffer);
  349.  
  350.             if (strlen(Line) > 0) {
  351.             WndwInputWindowPutStr(Line);
  352.             Line[0] = 0;
  353.             }
  354.             PrintObject(CrntObj);
  355.             CagdSetFloatFormat(GlblFloatFormat);
  356.             IritPrsrSetFloatFormat(GlblFloatFormat);
  357.             break;
  358.         default:
  359.             WndwInputWindowPutStr(
  360.             "PRINTF: Unknown % control to print command.");
  361.             return;
  362.         }
  363.     }
  364.     else if (CtlStr[i] == '\\') {
  365.         k = strlen(Line);
  366.  
  367.         switch (CtlStr[++i]) {
  368.         case 't':
  369.             Line[k] = '\t';
  370.             break;
  371.         case 'n':
  372.             Line[k] = '\n';
  373.             break;
  374.         case '%':
  375.             Line[k] = '%';
  376.             break;
  377.         }
  378.         Line[++k] = 0;
  379.  
  380.     }
  381.     else {
  382.         k = strlen(Line);
  383.         Line[k++] = CtlStr[i];
  384.         Line[k] = 0;
  385.     }
  386.     }
  387.     if (strlen(Line) > 0)
  388.     WndwInputWindowPutStr(Line);
  389. }
  390.  
  391. /*****************************************************************************
  392. *   Get the size of a list object.                         *
  393. *****************************************************************************/
  394. double GetListSize(IPObjectStruct *Obj)
  395. {
  396.     if (IP_IS_POLY_OBJ(Obj)) {
  397.     if (Obj -> U.Pl -> Pnext == NULL)
  398.         return (double) IritPrsrVrtxListLen(Obj -> U.Pl -> PVertex);
  399.     else
  400.         return (double) IritPrsrPolyListLen(Obj -> U.Pl);
  401.     }
  402.     else if (IP_IS_OLST_OBJ(Obj)) {
  403.     return (double) ListObjectLength(Obj);
  404.     }
  405.     else {
  406.     WndwInputWindowPutStr("None list object ignored.");
  407.         return 0.0;
  408.     }
  409. }
  410.  
  411. /*****************************************************************************
  412. *  Create an empty list.                             *
  413. *****************************************************************************/
  414. IPObjectStruct *GetNilList()
  415. {
  416.     IPObjectStruct
  417.     *PObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
  418.  
  419.     ListObjectInsert(PObj, 0, NULL);
  420.  
  421.     return PObj;
  422. }
  423.  
  424. /*****************************************************************************
  425. *   Get the nth object in a list.                         *
  426. *****************************************************************************/
  427. IPObjectStruct *GetNthList(IPObjectStruct *ListObj, RealType *Rn)
  428. {
  429.     int n = REAL_PTR_TO_INT(Rn);
  430.  
  431.     IPObjectStruct *PObj;
  432.  
  433.     if (!IP_IS_OLST_OBJ(ListObj)) {
  434.     WndwInputWindowPutStr("None list object ignored.");
  435.         return NULL;
  436.     }
  437.  
  438.     if (n < 1 || n > ListObjectLength(ListObj)) {
  439.     WndwInputWindowPutStr("Out of range of list.");
  440.         return NULL;
  441.     }
  442.  
  443.     PObj = CopyObject(NULL, ListObjectGet(ListObj, n - 1), FALSE);
  444.     return PObj;
  445. }
  446.  
  447. /*****************************************************************************
  448. *  Append two lists. Object reference is properly updated.             *
  449. *****************************************************************************/
  450. IPObjectStruct *AppendLists(IPObjectStruct *ListObj1, IPObjectStruct *ListObj2)
  451. {
  452.     int i, j;
  453.     IPObjectStruct *PObj, *PObjTmp;
  454.  
  455.     if (!IP_IS_OLST_OBJ(ListObj1) && !IP_IS_OLST_OBJ(ListObj2)) {
  456.     WndwInputWindowPutStr("None list object ignored.");
  457.     return NULL;
  458.     }
  459.  
  460.     PObj = IPAllocObject("", IP_OBJ_LIST_OBJ, NULL);
  461.  
  462.     for (i = 0; (PObjTmp = ListObjectGet(ListObj1, i)) != NULL; i++) {
  463.     ListObjectInsert(PObj, i, PObjTmp);
  464.     PObjTmp -> Count++;
  465.     }
  466.  
  467.     for (j = 0; (PObjTmp = ListObjectGet(ListObj2, j)) != NULL; i++, j++) {
  468.     ListObjectInsert(PObj, i, PObjTmp);
  469.     PObjTmp -> Count++;
  470.     }
  471.  
  472.     ListObjectInsert(PObj, i, NULL);
  473.  
  474.     return PObj;
  475. }
  476.  
  477. /*****************************************************************************
  478. *  Snoc (Cons to the end of the list, in place) the object to the list in    *
  479. * the second argument.                                 *
  480. *****************************************************************************/
  481. void SnocList(IPObjectStruct *PObj, IPObjectStruct *ListObj)
  482. {
  483.     int i;
  484.     IPObjectStruct *PObjTmp;
  485.  
  486.     if (!IP_IS_OLST_OBJ(ListObj)) {
  487.     WndwInputWindowPutStr("None list object ignored.");
  488.     return;
  489.     }
  490.  
  491.     i = ListObjectLength(ListObj);
  492.     ListObjectInsert(ListObj, i, PObjTmp = CopyObject(NULL, PObj, FALSE));
  493.     PObjTmp -> Count = 1;
  494.     ListObjectInsert(ListObj, i + 1, NULL);
  495. }
  496.  
  497. /*****************************************************************************
  498. *   Get object by its name - scans the object linear list.             *
  499. * Note the termination is also on 1000 objects (simple debugging aid in case *
  500. * the Object list became circular), and a fatal error is produced.         *
  501. *****************************************************************************/
  502. IPObjectStruct *GetObject(char *ObjName)
  503. {
  504.     int i = 0;
  505.     IPObjectStruct
  506.     *PObj = GlblObjList;
  507.  
  508.     while (PObj) {
  509.     if (strcmp(PObj -> Name, ObjName) == 0) {
  510.         return PObj;
  511.     }
  512.     PObj = PObj -> Pnext;
  513.     if (i++ >= 1000)
  514.         IritFatalError("GetObject: Global Object list too big (>1000)");
  515.     }
  516.     return NULL;
  517. }
  518.  
  519. /*****************************************************************************
  520. *  Extract a single entity out af an obj holding a set of entities at Index. *
  521. * For VECTOR, POINT, CTLPT, PLANE, MAT a single numeric data is returned.    *
  522. * For a POLYGON a single vertex is returned (as VECTOR).             *
  523. * For CURVE, and SURFACE a single control point is returned.             *
  524. *****************************************************************************/
  525. IPObjectStruct *GetObjectCoord(IPObjectStruct *PObj, RealType *RIndex)
  526. {
  527.     int Index = REAL_PTR_TO_INT(RIndex);
  528.     IPObjectStruct
  529.     *CoordPObj = NULL;
  530.     CagdCtlPtStruct CtlPt;
  531.  
  532.     switch (PObj -> ObjType) {
  533.     case IP_OBJ_POLY:
  534.         if (PObj -> U.Pl == NULL)
  535.         break;
  536.         if (PObj -> U.Pl -> Pnext) {
  537.         IPPolygonStruct *P;
  538.  
  539.         /* Extract a single poly from the poly list. */
  540.         for (P = PObj -> U.Pl;
  541.              P != NULL && Index > 0;
  542.              P = P -> Pnext, Index--);
  543.         if (P != NULL) {
  544.             IPPolygonStruct
  545.                 *PTmp = IPAllocPolygon(1, P -> Tags,
  546.                     CopyVertexList(P -> PVertex), NULL);
  547.  
  548.             CoordPObj = GenPolyObject("", PTmp, NULL);
  549.             PLANE_COPY(PTmp -> Plane, P -> Plane);
  550.             IP_RST_BBOX_POLY(PTmp);
  551.             CoordPObj -> Attrs = AttrCopyAttributes(PObj -> Attrs);
  552.  
  553.             if (IP_IS_POLYGON_OBJ(PObj))
  554.                 IP_SET_POLYGON_OBJ(CoordPObj);
  555.             else if (IP_IS_POLYLINE_OBJ(PObj))
  556.                 IP_SET_POLYLINE_OBJ(CoordPObj);
  557.             else if (IP_IS_POINTLIST_OBJ(PObj))
  558.                 IP_SET_POINTLIST_OBJ(CoordPObj);
  559.         }
  560.         }
  561.         else {
  562.         IPVertexStruct *V;
  563.  
  564.         /* Extract a vertex from the poly. */
  565.         for (V = PObj -> U.Pl -> PVertex;
  566.              V != NULL && Index > 0;
  567.              V = V -> Pnext, Index--);
  568.         if (V != NULL)
  569.           CoordPObj = GenVECObject(&V -> Coord[0], &V -> Coord[1],
  570.                        &V -> Coord[2]);
  571.         }
  572.         break;
  573.     case IP_OBJ_POINT:
  574.         if (Index >= 0 && Index < 3)
  575.         CoordPObj = GenNUMValObject(PObj -> U.Pt[Index]);
  576.         break;
  577.     case IP_OBJ_VECTOR:
  578.         if (Index >= 0 && Index < 3)
  579.         CoordPObj = GenNUMValObject(PObj -> U.Pt[Index]);
  580.         break;
  581.     case IP_OBJ_PLANE:
  582.         if (Index >= 0 && Index < 4)
  583.         CoordPObj = GenNUMValObject(PObj -> U.Pt[Index]);
  584.         break;
  585.     case IP_OBJ_CTLPT:
  586.         if (!CAGD_IS_RATIONAL_PT(PObj -> U.CtlPt.PtType) && Index == 0)
  587.         break;
  588.         if (Index >= 0 &&
  589.         Index <= CAGD_NUM_OF_PT_COORD(PObj -> U.CtlPt.PtType))
  590.         CoordPObj = GenNUMValObject(PObj -> U.CtlPt.Coords[Index]);
  591.         break;
  592.     case IP_OBJ_MATRIX:
  593.         if (Index >= 0 && Index < 16)
  594.         CoordPObj =
  595.             GenNUMValObject((*PObj -> U.Mat)[Index / 4][Index % 4]);
  596.         break;
  597.     case IP_OBJ_LIST_OBJ:
  598.         CoordPObj = GetNthList(PObj, RIndex);
  599.         break;
  600.     case IP_OBJ_CURVE:
  601.         if (PObj -> U.Crvs == NULL)
  602.             break;
  603.         if (PObj -> U.Crvs -> Pnext != NULL) {
  604.         CagdCrvStruct *Crv;
  605.  
  606.         /* Extract a single curve from the curve list. */
  607.         for (Crv = PObj -> U.Crvs;
  608.              Crv != NULL && Index > 0;
  609.              Crv = Crv -> Pnext, Index--);
  610.         if (Crv != NULL)
  611.             CoordPObj = GenCRVObject(CagdCrvCopy(Crv));
  612.         }
  613.         else {
  614.         /* Extract a ctlpt from the curve. */
  615.         CagdEditSingleCrvPt(PObj -> U.Crvs, &CtlPt, Index, FALSE);
  616.         CoordPObj = GenCTLPTObject(PObj -> U.Crvs -> PType,
  617.                        CtlPt.Coords, NULL);
  618.         }
  619.         break;
  620.     case IP_OBJ_SURFACE:
  621.         if (PObj -> U.Srfs == NULL)
  622.             break;
  623.         if (PObj -> U.Srfs -> Pnext != NULL) {
  624.         CagdSrfStruct *Srf;
  625.  
  626.         /* Extract a single surface from the surface list. */
  627.         for (Srf = PObj -> U.Srfs;
  628.              Srf != NULL && Index > 0;
  629.              Srf = Srf -> Pnext, Index--);
  630.         if (Srf != NULL)
  631.             CoordPObj = GenSRFObject(CagdSrfCopy(Srf));
  632.         }
  633.         else {
  634.         /* Extract a ctlpt from the surface. */
  635.         CagdEditSingleSrfPt(PObj -> U.Srfs, &CtlPt,
  636.                     Index / PObj -> U.Srfs -> ULength,
  637.                     Index % PObj -> U.Srfs -> ULength,
  638.                     FALSE);
  639.         CoordPObj = GenCTLPTObject(PObj -> U.Srfs -> PType,
  640.                        CtlPt.Coords, NULL);
  641.         }
  642.         break;
  643.     default:
  644.         break;
  645.     }
  646.  
  647.    if (CoordPObj == NULL)
  648.        WndwInputWindowPutStr("Coord: Out of range or wrong object type.");
  649.  
  650.     return CoordPObj;
  651. }
  652.  
  653.  
  654. /*****************************************************************************
  655. *   Free Object - delete it from global active list and free all it memory   *
  656. *****************************************************************************/
  657. void FreeObject(IPObjectStruct *PObj)
  658. {
  659.     /* Force free the object. Since the reference count should be actually   */
  660.     /* two (second for the parsing tree reference) we decrement it here.     */
  661.     if (PObj  -> Count == 2) {
  662.     PObj-> Count = 1;
  663.     DeleteObject(PObj, TRUE);
  664.     }
  665.     else {
  666.     /* Reduce the reference count by two - one for the parsing tree      */
  667.     /* this routine was called from and one for the fact this object     */
  668.     /* reference count is to be deleted since this routine was called.   */
  669.     DeleteObject(PObj, FALSE);
  670.     PObj -> Count -= 2;
  671.     }
  672. }
  673.  
  674. /*****************************************************************************
  675. *   Delete object by its pointer - scans the object linear list.         *
  676. * Note the deleted record is free only if Free = TRUE.                 *
  677. *****************************************************************************/
  678. void DeleteObject(IPObjectStruct *PObj, int Free)
  679. {
  680.     IPObjectStruct
  681.     *PObjScan = GlblObjList;
  682.  
  683.     if (GlblObjList == NULL)
  684.     return;
  685.  
  686.     if (PObj == GlblObjList) {       /* If it is the first one - special case. */
  687.     GlblObjList = GlblObjList->Pnext;
  688.     if (Free)
  689.         IPFreeObject(PObj);
  690.     return;
  691.     }
  692.  
  693.     while (PObjScan->Pnext) {
  694.     if (PObj == PObjScan->Pnext) {
  695.         PObjScan->Pnext = PObjScan->Pnext->Pnext;/* Delete it from list. */
  696.         if (Free)
  697.         IPFreeObject(PObj);                 /* And free it. */
  698.         return;
  699.     }
  700.     PObjScan = PObjScan->Pnext;
  701.     }
  702. }
  703.  
  704. /*****************************************************************************
  705. *   Insert object by its pointer - as first in object linear list.         *
  706. * Note it is assumed the object is not in the object list allready.         *
  707. *****************************************************************************/
  708. void InsertObject(IPObjectStruct *PObj)
  709. {
  710.     PObj -> Pnext = GlblObjList;
  711.     GlblObjList = PObj;
  712. }
  713.  
  714. /*****************************************************************************
  715. *   Print one line.                                 *
  716. *****************************************************************************/
  717. static void PrintOneLine(char *Line)
  718. {
  719.     WndwInputWindowPutStr(Line);
  720. }
  721.  
  722. /*****************************************************************************
  723. *   Print some usefull info on the given object.                 *
  724. *****************************************************************************/
  725. void PrintObject(IPObjectStruct *PObj)
  726. {
  727.     int Count = PObj -> Count;
  728.     CagdRType
  729.     DumpLvl = GetDumpLevel();
  730.     char Line[LINE_LEN_LONG],
  731.     *Name = (int) strlen(PObj -> Name) > 0 ? PObj -> Name : "NONE";
  732.  
  733.     CagdSetCagdFprintf(PrintOneLine);
  734.  
  735.     switch (PObj -> ObjType) {
  736.     case IP_OBJ_UNDEF:
  737.         sprintf(Line, "%-10s (%d) - Undefined type", Name, Count);
  738.         PrintOneLine(Line);
  739.         break;
  740.     case IP_OBJ_POLY:
  741.         sprintf(Line, "%-10s (%d) - Poly      type", Name, Count);
  742.         PrintOneLine(Line);
  743.         if (DumpLvl >= 3)
  744.         SaveObjectInFile(NULL, PObj);
  745.         break;
  746.     case IP_OBJ_NUMERIC:
  747.         sprintf(Line, "%-10s (%d) - Numeric   type", Name, Count);
  748.         PrintOneLine(Line);
  749.         if (DumpLvl >= 1)
  750.         SaveObjectInFile(NULL, PObj);
  751.         break;
  752.     case IP_OBJ_POINT:
  753.         sprintf(Line, "%-10s (%d) - Point     type", Name, Count);
  754.         PrintOneLine(Line);
  755.         if (DumpLvl >= 1)
  756.         SaveObjectInFile(NULL, PObj);
  757.         break;
  758.     case IP_OBJ_VECTOR:
  759.         sprintf(Line, "%-10s (%d) - Vector    type", Name, Count);
  760.         PrintOneLine(Line);
  761.         if (DumpLvl >= 1)
  762.         SaveObjectInFile(NULL, PObj);
  763.         break;
  764.     case IP_OBJ_PLANE:
  765.         sprintf(Line, "%-10s (%d) - Plane     type", Name, Count);
  766.         PrintOneLine(Line);
  767.         if (DumpLvl >= 1)
  768.         SaveObjectInFile(NULL, PObj);
  769.         break;
  770.     case IP_OBJ_CTLPT:
  771.         sprintf(Line, "%-10s (%d) - CtlPt     type", Name, Count);
  772.         PrintOneLine(Line);
  773.         if (DumpLvl >= 1)
  774.         SaveObjectInFile(NULL, PObj);
  775.         break;
  776.     case IP_OBJ_MATRIX:
  777.         sprintf(Line, "%-10s (%d) - Matrix    type", Name, Count);
  778.         PrintOneLine(Line);
  779.         if (DumpLvl >= 1)
  780.         SaveObjectInFile(NULL, PObj);
  781.         break;
  782.     case IP_OBJ_STRING:
  783.         sprintf(Line, "%-10s (%d) - String    type", Name, Count);
  784.         PrintOneLine(Line);
  785.         if (DumpLvl >= 1)
  786.         SaveObjectInFile(NULL, PObj);
  787.         break;
  788.     case IP_OBJ_LIST_OBJ:
  789.         sprintf(Line, "%-10s (%d) - Object List type", Name, Count);
  790.         PrintOneLine(Line);
  791.         if (DumpLvl >= 4)
  792.         SaveObjectInFile(NULL, PObj);
  793.         break;
  794.     case IP_OBJ_CURVE:
  795.         sprintf(Line, "%-10s (%d) - Curve     type", Name, Count);
  796.         PrintOneLine(Line);
  797.         if (DumpLvl >= 2) {
  798.         SaveObjectInFile(NULL, PObj);
  799.         }
  800.         break;
  801.     case IP_OBJ_SURFACE:
  802.         sprintf(Line, "%-10s (%d) - Surface   type", Name, Count);
  803.         PrintOneLine(Line);
  804.         if (DumpLvl >= 2) {
  805.         SaveObjectInFile(NULL, PObj);
  806.         }
  807.         break;
  808.     default:
  809.         sprintf(Line, "%-10s (%d) - Obj type error, type = %d",
  810.                 Name, Count, PObj->ObjType);
  811.         PrintOneLine(Line);
  812.         break;
  813.     }
  814.  
  815.     CagdSetCagdFprintf(NULL);
  816. }
  817.  
  818. /*****************************************************************************
  819. *   Print some usefull info on all the given objects.                 *
  820. *****************************************************************************/
  821. void PrintObjectList(IPObjectStruct *PObj)
  822. {
  823.     PrintOneLine("");
  824.  
  825.     while (PObj != NULL) {
  826.     PrintObject(PObj);
  827.     PObj = PObj -> Pnext;
  828.     }
  829. }
  830.  
  831. /*****************************************************************************
  832. *  Coerce an object to a new object.                         *
  833. *****************************************************************************/
  834. IPObjectStruct *CoerceObjectTo(IPObjectStruct *PObj, RealType *RNewType)
  835. {
  836.     int i,
  837.     NewType = REAL_TO_INT(*RNewType);
  838.     RealType Pt[CAGD_MAX_PT_SIZE], *R;
  839.     IPObjectStruct
  840.     *NewObj = NULL;
  841.  
  842.     switch (PObj -> ObjType) {
  843.     case IP_OBJ_POINT:
  844.     case IP_OBJ_VECTOR:
  845.     case IP_OBJ_PLANE:
  846.     case IP_OBJ_CTLPT:
  847.         Pt[0] = 1.0;
  848.         for (i = 1; i < CAGD_MAX_PT_SIZE; i++)
  849.             Pt[i] = 0.0;
  850.         switch (PObj -> ObjType) {
  851.         case IP_OBJ_POINT:
  852.             PT_COPY(&Pt[1], PObj -> U.Pt);
  853.             break;
  854.         case IP_OBJ_VECTOR:
  855.             PT_COPY(&Pt[1], PObj -> U.Vec);
  856.             break;
  857.         case IP_OBJ_PLANE:
  858.             PLANE_COPY(&Pt[1], PObj -> U.Plane);
  859.             break;
  860.         case IP_OBJ_CTLPT:
  861.             R = PObj -> U.CtlPt.Coords;
  862.             CagdCoercePointTo(&Pt[1], CAGD_PT_E5_TYPE,
  863.                       &R, -1, PObj -> U.CtlPt.PtType);
  864.             break;
  865.         default:
  866.             break;
  867.         }
  868.  
  869.         switch (NewType) {
  870.         case IP_OBJ_POINT:
  871.             NewObj = GenPTObject(&Pt[1], &Pt[2], &Pt[3]);
  872.             break;
  873.         case IP_OBJ_VECTOR:
  874.             NewObj = GenVECObject(&Pt[1], &Pt[2], &Pt[3]);
  875.             break;
  876.         case IP_OBJ_PLANE:
  877.             NewObj = GenPLANEObject(&Pt[1], &Pt[2], &Pt[3], &Pt[4]);
  878.             break;
  879.         case IP_OBJ_CTLPT:
  880.             NewObj = GenCTLPTObject(CAGD_PT_E3_TYPE, NULL, Pt);
  881.             break;
  882.         case CAGD_PT_P1_TYPE:
  883.         case CAGD_PT_P2_TYPE:
  884.         case CAGD_PT_P3_TYPE:
  885.         case CAGD_PT_P4_TYPE:
  886.         case CAGD_PT_P5_TYPE:
  887.             if (PObj -> ObjType == IP_OBJ_CTLPT)
  888.                 CagdCoercePointTo(Pt, CAGD_PT_P5_TYPE,
  889.                       &R, -1, PObj -> U.CtlPt.PtType);
  890.         case CAGD_PT_E1_TYPE:
  891.         case CAGD_PT_E2_TYPE:
  892.         case CAGD_PT_E3_TYPE:
  893.         case CAGD_PT_E4_TYPE:
  894.         case CAGD_PT_E5_TYPE:
  895.             NewObj = GenCTLPTObject(NewType, NULL, Pt);
  896.             break;            
  897.         default:
  898.             break;            
  899.         }
  900.         break;
  901.     case IP_OBJ_CURVE:
  902.         switch (NewType) {
  903.         case CAGD_PT_E1_TYPE:
  904.         case CAGD_PT_E2_TYPE:
  905.         case CAGD_PT_E3_TYPE:
  906.         case CAGD_PT_E4_TYPE:
  907.         case CAGD_PT_E5_TYPE:
  908.         case CAGD_PT_P1_TYPE:
  909.         case CAGD_PT_P2_TYPE:
  910.         case CAGD_PT_P3_TYPE:
  911.         case CAGD_PT_P4_TYPE:
  912.         case CAGD_PT_P5_TYPE:
  913.             NewObj = GenCRVObject(CagdCoerceCrvTo(PObj -> U.Crvs,
  914.                               NewType));
  915.             break;
  916.         default:
  917.             break;
  918.         }
  919.         break;
  920.     case IP_OBJ_SURFACE:
  921.         switch (NewType) {
  922.         case CAGD_PT_E1_TYPE:
  923.         case CAGD_PT_E2_TYPE:
  924.         case CAGD_PT_E3_TYPE:
  925.         case CAGD_PT_E4_TYPE:
  926.         case CAGD_PT_E5_TYPE:
  927.         case CAGD_PT_P1_TYPE:
  928.         case CAGD_PT_P2_TYPE:
  929.         case CAGD_PT_P3_TYPE:
  930.         case CAGD_PT_P4_TYPE:
  931.         case CAGD_PT_P5_TYPE:
  932.             NewObj = GenSRFObject(CagdCoerceSrfTo(PObj -> U.Srfs,
  933.                               NewType));
  934.             break;
  935.         default:
  936.             break;
  937.         }
  938.  
  939.         break;
  940.     default:
  941.         break;
  942.     }
  943.  
  944.     if (NewObj == NULL)
  945.     WndwInputWindowPutStr("Invalid coerction requested.");
  946.     return NewObj;
  947. }
  948.  
  949. /*****************************************************************************
  950. *  Save an object in a data file                         *
  951. *****************************************************************************/
  952. void SaveObjectInFile(char *FileName, IPObjectStruct *PObj)
  953. {
  954.     FILE *f;
  955.     int IsBinary = FALSE;
  956.     char FullFileName[LINE_LEN_LONG];
  957.  
  958.     if (FileName != NULL) {
  959.     strcpy(FullFileName, FileName);
  960.     if (strstr(FullFileName, ".bdt") ||
  961.         strstr(FullFileName, ".BDT")) {
  962.         /* Binary data file is requested. */
  963.         IsBinary = TRUE;
  964.     }
  965.     else if (strstr(FullFileName, ".dat") == NULL &&
  966.          strstr(FullFileName, ".DAT") == NULL)
  967.         strcat(FullFileName, ".dat");
  968.  
  969.     if ((f = IritPrsrOpenDataFile(FullFileName, FALSE, FALSE)) != NULL) {
  970.         if (!IsBinary)
  971.         IRIT_DATA_HEADER(f, "Irit");
  972.  
  973.         IritPrsrPutObject(f, PObj);
  974.         IritPrsrCloseDataFile(f);
  975.     }
  976.     else
  977.         WndwInputWindowPutStr("failed to open file for write.");
  978.     }
  979.     else {
  980.     IritPrsrSetPrintFunc(WndwInputWindowPutStr);
  981.     IritPrsrPutObject(NULL, PObj);
  982.     }
  983. }
  984.  
  985. /*****************************************************************************
  986. *  Load an object(s) from a data file                         *
  987. *****************************************************************************/
  988. IPObjectStruct *LoadObjectFromFile(char *FileName)
  989. {
  990.     FILE *f;
  991.     char FullFileName[LINE_LEN_LONG];
  992.  
  993.     if (FileName != NULL) {
  994.     strcpy(FullFileName, FileName);
  995.     if (strstr(FullFileName, ".bdt") == NULL &&
  996.         strstr(FullFileName, ".BDT") == NULL &&
  997.         strstr(FullFileName, ".dat") == NULL &&
  998.         strstr(FullFileName, ".DAT") == NULL)
  999.         strcat(FullFileName, ".dat");
  1000.  
  1001.     if ((f = IritPrsrOpenDataFile(FullFileName, TRUE, FALSE)) != NULL) {
  1002.             IPObjectStruct
  1003.                 *PObj = IritPrsrGetObjects(f);
  1004.  
  1005.         UpdateLoadedHierarchy(PObj, PObj);
  1006.  
  1007.         IritPrsrCloseDataFile(f);
  1008.         return PObj;
  1009.     }
  1010.     }
  1011.  
  1012.     WndwInputWindowPutStr("Failed to open file for read.");
  1013.     return NULL;
  1014. }
  1015.  
  1016. /*****************************************************************************
  1017. *  Update hierarchy of loaded object.                         *
  1018. *****************************************************************************/
  1019. static void UpdateLoadedHierarchy(IPObjectStruct *PObj, IPObjectStruct *Root)
  1020. {
  1021.     if (IP_IS_OLST_OBJ(PObj)) {
  1022.     int i;
  1023.     IPObjectStruct *PObjTmp;
  1024.  
  1025.     for (i = 0; (PObjTmp = ListObjectGet(PObj, i)) != NULL; i++)
  1026.         UpdateLoadedHierarchy(PObjTmp, Root);
  1027.     }
  1028.  
  1029.     if (PObj != Root && strlen(PObj -> Name) > 0) {
  1030.     IPObjectStruct *OldPObj;
  1031.  
  1032.     if ((OldPObj = GetObject(PObj -> Name)) != NULL)
  1033.         DeleteObject(OldPObj, TRUE);
  1034.     InsertObject(PObj);
  1035.     PObj -> Count++;
  1036.     }
  1037. }
  1038.  
  1039. /*****************************************************************************
  1040. *  Save an object in a file                             *
  1041. *****************************************************************************/
  1042. int LoadSaveObjectParseError(char **ErrorMsg)
  1043. {
  1044.     int RetVal = IritPrsrParseError(ErrorMsg);
  1045.  
  1046.     if (!RetVal) {
  1047.     *ErrorMsg = "";
  1048.     return FALSE;
  1049.     }
  1050.     else
  1051.     return TRUE;
  1052. }
  1053.  
  1054. /*****************************************************************************
  1055. * Routine to scale an object according to XYZ scaling vector Vec.         *
  1056. *****************************************************************************/
  1057. IPObjectStruct *GenMatObjectGeneric(IPObjectStruct *LstObjList)
  1058. {
  1059.     MatrixType Mat;
  1060.  
  1061.     /* Generate the transformation matrix */
  1062.     if (MatGenMatGeneric(LstObjList, Mat))
  1063.     return GenMATObject(Mat);
  1064.     else
  1065.     return NULL;
  1066. }
  1067.  
  1068. /*****************************************************************************
  1069. * Routine to generate a 4*4 matrix  by specifying all its 16 coefficients.   *
  1070. *****************************************************************************/
  1071. static int MatGenMatGeneric(IPObjectStruct *LstObjList, MatrixType Mat)
  1072. {
  1073.     int i, j;
  1074.     IPObjectStruct *Row, *Col;
  1075.  
  1076.     MatGenUnitMat(Mat);                             /* Make it unit matrix, */
  1077.  
  1078.     if (!IP_IS_OLST_OBJ(LstObjList)) {
  1079.     WndwInputWindowPutStr("Matrix: Not object list object!");
  1080.     return FALSE;
  1081.     }
  1082.  
  1083.     for (i = 0; i < 4; i++) {
  1084.     if ((Row = ListObjectGet(LstObjList, i)) == NULL) {
  1085.         WndwInputWindowPutStr("Matrix: Four rows expected, found less");
  1086.         return FALSE;
  1087.     }
  1088.     if (!IP_IS_OLST_OBJ(Row)) {
  1089.         WndwInputWindowPutStr("None list object found in list");
  1090.         return FALSE;
  1091.     }
  1092.  
  1093.     for (j = 0; j < 4; j++) {
  1094.         if ((Col = ListObjectGet(Row, j)) == NULL) {
  1095.         WndwInputWindowPutStr("Matrix: Four columns expected, found less.");
  1096.         return FALSE;
  1097.         }
  1098.  
  1099.         if (!IP_IS_NUM_OBJ(Col)) {
  1100.         WndwInputWindowPutStr("Numeric value expected.");
  1101.         return FALSE;
  1102.         }
  1103.  
  1104.         Mat[i][j] = Col -> U.R;
  1105.     }
  1106.     }
  1107.     return TRUE;
  1108. }
  1109.  
  1110. /*****************************************************************************
  1111. * Routine to handle all surfaces/curves. Simple chain them into a linear     *
  1112. * list and return it. A Call back function from IritPrsr.             *
  1113. *****************************************************************************/
  1114. IPObjectStruct *IritPrsrProcessFreeForm(IPObjectStruct *CrvObjs,
  1115.                     IPObjectStruct *SrfObjs)
  1116. {
  1117.     if (CrvObjs == NULL)
  1118.     return SrfObjs;
  1119.     else if (SrfObjs == NULL)
  1120.     return CrvObjs;
  1121.     else {
  1122.     IPObjectStruct *PTmp = CrvObjs;
  1123.  
  1124.     while (PTmp -> Pnext)
  1125.         PTmp = PTmp -> Pnext;
  1126.     PTmp -> Pnext = SrfObjs;
  1127.     return CrvObjs;
  1128.     }
  1129. }
  1130.  
  1131.